#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>  /* for sei() */
#include <util/delay.h>     /* for _delay_ms() */

#include <avr/pgmspace.h>   /* required by usbdrv.h */
#include "usbdrv.h"

/* ------------------------------------------------------------------------- */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */

PROGMEM char usbHidReportDescriptor[35] = {
    0x05, 0x0c,                    // USAGE_PAGE (Consumer Devices)
    0x09, 0x01,                    // USAGE (Consumer Control)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x0c,                    //   USAGE_PAGE (Consumer Devices)
    0x09, 0xb0,                    //   USAGE (Play)
    0x09, 0xb5,                    //   USAGE (Scan Next Track)
    0x09, 0xb6,                    //   USAGE (Scan Previous Track)
    0x09, 0xe2,                    //   USAGE (Mute)
    0x09, 0xb7,                    //   USAGE (Stop)
    0x09, 0xb2,                    //   USAGE (Record)
    0x09, 0xb3,                    //   USAGE (Fast Forward)
    0x09, 0xb4,                    //   USAGE (Rewind)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0xc0                           // END_COLLECTION
};

unsigned char inp;
static unsigned char reportBuffer;
static uchar idleRate;   /* repeat rate for keyboards, never used for mice */

#define reportBufferPtr (void*)&reportBuffer

void buildReportBuffer(void)
{
	inp = PINB;
	reportBuffer = (~inp) & 7;
}

/* ------------------------------------------------------------------------- */

usbMsgLen_t usbFunctionSetup(uchar data[8])
{
	usbRequest_t *rq = (void *)data;

	/* The following requests are never used. But since they are required by
	* the specification, we implement them in this example.
	*/
	if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS)
	{    /* class request type */
		if(rq->bRequest == USBRQ_HID_GET_REPORT)
		{
			/* wValue: ReportType (highbyte), ReportID (lowbyte) */
			/* we only have one report type, so don't look at wValue */
			usbMsgPtr = reportBufferPtr;
			return sizeof(reportBuffer);
		}
		else if(rq->bRequest == USBRQ_HID_GET_IDLE)
		{
			usbMsgPtr = &idleRate;
			return 1;
		}
		else if(rq->bRequest == USBRQ_HID_SET_IDLE)
		{
			idleRate = rq->wValue.bytes[1];
		}
	}
	else
	{
		/* no vendor specific requests implemented */
	}
	return 0;   /* default for not implemented requests: return no data back to host */
}

/* ------------------------------------------------------------------------- */

int main(void)
{
	uchar i;

	//wdt_enable(WDTO_1S);
	/* Even if you don't use the watchdog, turn it off here. On newer devices,
	* the status of the watchdog (on/off, period) is PRESERVED OVER RESET!
	*/
	
	/* RESET status: all port bits are inputs without pull-up.
	* That's the way we need D+ and D-. Therefore we don't need any
	* additional hardware initialization.
	*/
	usbInit();
	usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */
	i = 0;
	
	while(--i)
	{             /* fake USB disconnect for > 250 ms */
		//wdt_reset();
		_delay_ms(1);
	}
	
	PORTB = 0x7;
	
	usbDeviceConnect();
	sei();
	
	for(;;)
	{                /* main event loop */
		//wdt_reset();
		usbPoll();
		if(usbInterruptIsReady() && (inp != PINB))
		{
			buildReportBuffer();
			/* called after every poll of the interrupt endpoint */
			usbSetInterrupt(reportBufferPtr, sizeof(reportBuffer));
		}
	}
	return 0;
}

/* ------------------------------------------------------------------------- */
